#    Copyright 2012 Tri-County Electric Cooperative, St. Matthews, South Carolina
#    This file is part of COOPWeb.
#    COOPWeb is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#    COOPWeb is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    You should have received a copy of the GNU General Public License
#    along with COOPWeb.  If not, see <http://www.gnu.org/licenses/>.

import sys
NeededPaths=['/opt/coopweb/djangoproject/include', '/opt/coopweb/djangoproject', '/opt']
for path in NeededPaths:
    if(not path in sys.path):
        sys.path.append(path)
import httplib
import os, csv, shutil, datetime, time, reportlab, djangoproject.settings, datetime
from django.contrib.gis.db import models
from django.contrib.gis.geos import *
from django.http import HttpResponse
from django.contrib.gis import admin
import django, urllib
import COOPWeb_Form
from ftplib import FTP
import cPickle
import time
from django.db import transaction
import django.contrib.auth
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
import base64
from scapy.all import *
import time





class CommunicationCommandName(models.Model):
    Name=models.TextField(db_column='name', unique=True)    
    class Meta:
        db_table=u'communicationcommandname'
        app_label='djangoproject'
    def __unicode__(self):
        return self.Name
    @staticmethod
    def GetOrCreate(Name):
        CommunicationCommandNames=CommunicationCommandName.objects.filter(Name=Name)
        if(CommunicationCommandNames):
            return CommunicationCommandNames[0]
        else:
            New=CommunicationCommandName()
            New.Name=Name
            New.save()
            return New
        


class CommunicationCommand(models.Model):
    CommunicationCommandName=models.ForeignKey('CommunicationCommandName', db_column='communicationcommandname_id')
    CommandString=models.TextField(db_column='commandstring', null=True, blank=True)
    CreateDateTime=models.DateTimeField(db_column='createdatetime')
    PickedUpByCOOPIPDateTime=models.DateTimeField(db_column='pickedupbycoopipdatetime', null=True, blank=True)
    ResponseByCOOPIPDateTime=models.DateTimeField(db_column='responsebycoopipdatetime', null=True, blank=True)
    ResponseString=models.TextField(db_column='responsestring', null=True, blank=True)
    CreateUsername=models.TextField(db_column='createusername', null=True, blank=True)
    class Meta:
        db_table=u'communicationcommand'
        app_label='djangoproject'
    @staticmethod
    def CreatePingMeterCommand(MeterNumber, Username):
        #Name:  'PingMeter'
        #String:  Meter #'s separated by commas
        #ResponseString:  Meter#'s + ':' + 'Success' or 'Failure'
        New=CommunicationCommand()
        New.CommunicationCommandName=CommunicationCommandName.GetOrCreate('PingMeter')
        New.CreateDateTime=datetime.datetime.now()
        New.CommandString=str(MeterNumber).strip()
        New.CreateUsername = Username
        New.save()
    @staticmethod
    def CreateAlarmEventAcknowledgeCommand(Alarmid, Description, Username):
        Description = Description.replace('|', '') #CommandString will be in format Alarmid|Description, so strip out any pipes in case they were used
        New=CommunicationCommand()
        New.CommunicationCommandName=CommunicationCommandName.GetOrCreate('AlarmEventAcknowledge')
        New.CreateDateTime=datetime.datetime.now()
        New.CommandString = str(Alarmid) + '|' + str(Description)
        New.CreateUsername = Username
        New.save()
    @staticmethod
    def CreateAlarmEventCloseCommand(Alarmid, Description, Username):
        Description = Description.replace('|', '') #CommandString will be in format Alarmid|Description, so strip out any pipes in case they were used
        New=CommunicationCommand()
        New.CommunicationCommandName=CommunicationCommandName.GetOrCreate('AlarmEventClose')
        New.CreateDateTime=datetime.datetime.now()
        New.CommandString = str(Alarmid) + '|' + str(Description)
        New.CreateUsername = Username
        New.save()
    @staticmethod
    def CreateAlarmArmCommand(Alarmid, Description, Username):
        Description = Description.replace('|', '') #CommandString will be in format Alarmid|Description, so strip out any pipes in case they were used
        New=CommunicationCommand()
        New.CommunicationCommandName=CommunicationCommandName.GetOrCreate('AlarmArm')
        New.CreateDateTime=datetime.datetime.now()
        New.CommandString = str(Alarmid) + '|' + str(Description)
        New.CreateUsername = Username
        New.save()
    @staticmethod
    def CreateAlarmDisarmCommand(Alarmid, Description, Username):
        Description = Description.replace('|', '') #CommandString will be in format Alarmid|Description, so strip out any pipes in case they were used
        New=CommunicationCommand()
        New.CommunicationCommandName=CommunicationCommandName.GetOrCreate('AlarmDisarm')
        New.CreateDateTime=datetime.datetime.now()
        New.CommandString = str(Alarmid) + '|' + str(Description)
        New.CreateUsername = Username
        New.save()

    





class COOPWebCOOPIPAuthenticate(models.Model):
    """Table used during authentication, COOPIP will look for pending authentication requests and attempt to authenticate against the credentials in this table,
       Data is deleted from this table on an ongoing basis"""
    CreateDateTime=models.DateTimeField(db_column='createdatetime')
    Username=models.TextField(db_column='username', null=True, blank=True)
    Password=models.TextField(db_column='password', null=True, blank=True)
    COOPIPAuthenticationSuccessIndicator=models.NullBooleanField(db_column='coopipauthenticationsuccessindicator', null=True, blank=True)  #Null = Unknown, True = Authenticate, False = No Authenticate
    class Meta:
        db_table=u'coopwebcoopipauthenticate'
        app_label='djangoproject'
    @staticmethod
    def Authenticate(request):
        COOPWebCOOPIPAuthenticate.CleanOldAuthentications()
        if not request.user.is_anonymous():
            return True, request.user.username
        try:
            Username=request.POST['Username']
            Password=request.POST['Password']
        except:
            Username, Password = '', ''
        
        if(len(Username) < 4) or (len(Password) < 4):
            return False, 'Not Logged In'
        DebugMessage, Username, LoginIndicator = COOPWebCOOPIPAuthenticate.AttemptNewAuthentication(Username, Password)
        if(LoginIndicator):
            COOPWebCOOPIPAuthenticate.CreateAndAuthenticateUser(request, Username)
        return LoginIndicator, Username


    @staticmethod
    def CreateAndAuthenticateUser(request, Username):
        try:
            user = User.objects.get(username__exact=Username)
        except:
            user = User.objects.create_user(Username, 'none@tce.coop', 'FakePasswordNotUsed_____124124512')
            user.is_active=True
            user.save()
        user = django.contrib.auth.authenticate(username=Username, password='FakePasswordNotUsed_____124124512')
        login(request, user)

    @staticmethod
    def CleanOldAuthentications():
        StartDate=datetime.datetime.now() - datetime.timedelta(minutes=5)
        cOOPWebCOOPIPAuthenticates = COOPWebCOOPIPAuthenticate.objects.filter(CreateDateTime__lte = StartDate)
        for i in cOOPWebCOOPIPAuthenticates:
            i.delete()
        StartDate=datetime.datetime.now() + datetime.timedelta(minutes=5)  #in case records ever got a future datetime for any reason
        cOOPWebCOOPIPAuthenticates = COOPWebCOOPIPAuthenticate.objects.filter(CreateDateTime__gte = StartDate)
        for i in cOOPWebCOOPIPAuthenticates:
            i.delete()

    @staticmethod
    @transaction.commit_manually
    def AttemptNewAuthentication(Username, Password):
        New=COOPWebCOOPIPAuthenticate()
        New.CreateDateTime=datetime.datetime.now()
        New.Username=Username
        New.Password=Password
        New.save()
        transaction.commit()
        id = New.id
        DelayInSeconds = 5
        SecondsWaited = 0
        while(True):
           try:           
               Current=COOPWebCOOPIPAuthenticate.objects.filter(id = id)[0]
           except KeyError:
               transaction.commit()
               return 'Not logged in, KeyError', '', False
           if(Current.COOPIPAuthenticationSuccessIndicator == True):
               Current.delete()
               transaction.commit()
               return 'Logged In', Username, True
           if(Current.COOPIPAuthenticationSuccessIndicator == False):
               Current.delete()
               transaction.commit()
               return 'Invalid Username or Password', '', False
           if(Current.COOPIPAuthenticationSuccessIndicator == None):
               SecondsWaited = SecondsWaited + DelayInSeconds
               if(SecondsWaited > 14 * DelayInSeconds):
                   Current.delete()
                   transaction.commit()
                   return 'Not logged in, TimeOut', '', False
               time.sleep(DelayInSeconds)
               

class Alarm(models.Model):
    Alarmid=models.IntegerField(db_column='alarmid', unique=True)
    SubstationName=models.TextField(db_column='substationname', null=True, blank=True)
    AlarmStatus=models.TextField(db_column='alarmstatus', null=True, blank=True)
    CurrentEventid=models.IntegerField(db_column='currenteventid', null=True, blank=True)
    CurrentEventAcknowledgeIndicator=models.NullBooleanField(db_column='currenteventacknowledgeindicator', null=True, blank=True)
    CurrentEventNote=models.TextField(db_column='currenteventnote', null=True, blank=True)
    CurrentEventStartDateTime=models.DateTimeField(db_column='currenteventstartdatetime', null=True, blank=True)
    CurrentEventAcknowledgeDateTime=models.DateTimeField(db_column='currenteventacknowledgedatetime', null=True, blank=True)
    UpdateFromCOOPIPDateTime=models.DateTimeField(db_column='updatefromcoopipdatetime')
    class Meta:
        db_table=u'alarm'
        app_label='djangoproject'

class AVLSystemNetworkTraffic(models.Model):
    """Packet log files generated by daemonlogger; multiple sources over UDP"""
    CreateDateTime=models.DateTimeField(db_column='createdatetime')
    PacketCapture=models.TextField(db_column='packetcaptureblob', null=True, blank=True)
    class Meta:
        db_table=u'avlsystemtraffic'
        app_label='djangoproject'

class AVLSystemLog(models.Model):
    """Individual packet from a single AVL system"""
    using='default'
    CreateDateTime=models.DateTimeField(db_column='createdatetime', null=True, blank=True)
    AVLSystemNetworkTrafficid=models.IntegerField(db_column='avlsystemnetworktrafficid', null=True, blank=True)
    RawTAIP=models.TextField(db_column='rawtaip',null=True, blank=True)
    Latitude=models.FloatField(db_column='latitude',null=True, blank=True)
    Longitude=models.FloatField(db_column='longitude',null=True, blank=True)
    Speed=models.IntegerField(db_column='speed',null=True, blank=True)
    Bearing=models.IntegerField(db_column='bearing',null=True, blank=True)
    VehicleNumber=models.TextField(db_column='vehiclenumber',null=True, blank=True)
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'avlsystemlog'
        app_label='djangoproject'
    @staticmethod
    def CreateLogs(AVLTraffic):
        PacketData = AVLSystemLog.DecodePackets(AVLTraffic.PacketCapture)
        for SourceIPAddress, TAIPString in PacketData:
            new = AVLSystemLog()
            new.AVLSystemNetworkTrafficid = AVLTraffic.id
            new.CreateDateTime = datetime.datetime.now()
            new.RawTAIP = TAIPString
            new.save()
    @staticmethod
    def DecodePackets(EncodedBinaryData):
        DecodedBinaryData = base64.decodestring(EncodedBinaryData)
        TempfileName = '/opt/coopweb/scripts/AVLSystemLogTempFile' + str(time.time())
        Tempfile = open(TempfileName, 'wb')
        Tempfile.write(DecodedBinaryData)
        Tempfile.close()
        Packets = rdpcap(TempfileName)
        ReturnObject = []
        for packet in Packets:
            SourceIPAddress = packet.getlayer(IP).src
            TAIPString = packet.load
            ReturnObject.append((SourceIPAddress, TAIPString))
        #if it can't be removed, leave it; error handling if needed
        os.remove(TempfileName)
        return ReturnObject
    @staticmethod
    def ParseTAIP(All=False):
        #TAIP is fixed length
        #ReportPositionVelocity Time(discard) latitude longitude speed bearing source age; ID
        #>RPV 50589 +3365107 -08080241 000 000 32;ID=0103<
        if All:
            Logs = AVLSystemLog.objects.all()
        else:
            Logs = AVLSystemLog.objects.filter(Latitude__isnull=True)
        for i in Logs:
            try:
                Input = i.RawTAIP
                i.Latitude = float(Input[9:12] + '.' + Input[12:17])
                i.Longitude = float(Input[17:21] + '.' + Input[21:26])
                i.Speed = int(Input[26:29])
                i.Bearing = int(Input[29:32])
                i.VehicleNumber = Input[38:42]
                i.save()
            except:
                pass #likely malformed
    @staticmethod
    def CalculatePoints(All=False):
        if All:
            Logs = AVLSystemLog.objects.all()
        else:
            Logs = AVLSystemLog.objects.exclude(Latitude__isnull=True).filter(Point__isnull=True)
        for i in Logs:
            StartingPoint = Point(i.Longitude, i.Latitude)
            StartingPoint.srid=4326
            Transformed = StartingPoint.transform(2273, clone=True)
            i.Point = Transformed
            i.save()


        

